﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using System.Runtime.Serialization;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Cfg.ConfigurationSchema;
using NHibernate.Impl;
using NHibernate.Mapping.Attributes;
using NHibernate.Tool.hbm2ddl;
using FlushMode = NHibernate.FlushMode;

namespace monitor_2_DBwork
{
    interface IRepositoryName
    {        
        string Name { get; set; }
    }

    interface IRepositoryDevicelibInherit
    {
        int Iddevicelib { get; set; }
        int Idrtps { get; set; }
    }

    interface IRepositoryDeviceInherit
    {
        int Iddevice { get; set; }
    }

    interface IRepositoryUniqueRequest
    {
        string UniqueRequestString { get; }
    }

    class NHibernateHelper : IDisposable
    {
        readonly ISessionFactory _sessionFactory;
        static NHibernateHelper _nHibHelper;

        private NHibernateHelper()
        {
            var config = new Configuration().Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources\\hibernate.cfg.xml"));
            HbmSerializer.Default.Validate = true;
            var assembly = Assembly.GetAssembly(typeof(RTPS));
            HbmSerializer.Default.HbmAssembly = assembly.FullName;
            HbmSerializer.Default.HbmNamespace = typeof(RTPS).Namespace;
            var stream = HbmSerializer.Default.Serialize(assembly);
            config.AddInputStream(stream);
            new SchemaUpdate(config).Execute(false, true);
            _sessionFactory = config.BuildSessionFactory();

            //var cfg = new Configuration();
            //cfg.Configure("Resources\\hibernate.cfg.xml");
            //cfg.AddAssembly(typeof(NHibernateHelper).Assembly);
            //new SchemaUpdate(cfg).Execute(false, true);
            //_sessionFactory = cfg.BuildSessionFactory();
        }

        public static NHibernateHelper Create()
        {
            if (_nHibHelper == null)
            {
                try
                {
                    _nHibHelper = new NHibernateHelper();
                }
                catch (Exception)
                {
                    _nHibHelper = null;
                }
            }
            return _nHibHelper;
        }

        public ISession Session
        {
            get { return _sessionFactory.OpenSession(); }
        }

        public void Dispose()
        {
            if (!_sessionFactory.IsClosed)
                _sessionFactory.Close();
        }

    }

    public interface IRepository<T> where T:class
    {
        bool Add(T item, int id);

        bool AddRange(List<T> items);

        bool Remove(T item);

        bool Update(T item, string name, int idrtps, int iddevice);

        T GetItem(int itemId);

        T GetItem(string name/*, int iddevice*/, int idrtps);

        long GetItemCount();

        List<T> GetList(string criteria);

        List<T> GetListStart(string criteria);

        void SessionClose();
    }

    public class DBRepository<T> : IRepository<T>, IDisposable where T:class, new()
    {
        private ISession _session;

        public bool IsSessionOpened
        {
            get { return _session.IsOpen; }
        }

        public static bool CheckConnection()
        {
            var h = NHibernateHelper.Create();
            using (h.Session)
            {
            }
            return true;
            
        }

        public bool Add(T item, int id = -1)
        {
            try
            {
                using (var session = NHibernateHelper.Create().Session)
                {
                    using (var trans = session.BeginTransaction())
                    {
                        try
                        {
                            if (id != -1)
                                session.Save(item, id);
                            else
                                session.Save(item);
                            trans.Commit();
                        }
                        catch (Exception ex)
                        {
                            if (ex.InnerException != null)
                            {
                                if (!ex.InnerException.Message.Contains(" not unique"))
                                {
                                    trans.Rollback();
                                    return false;
                                }
                            }
                            else
                            {
                                trans.Rollback();
                                return false;
                            }
                            
                        }
                        
                    }
                }
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        public bool AddRange(List<T> items)
        {
            var additems = new List<T>();
            var dt1 = DateTime.Now;
            bool repeats = false;
            try
            {
                #region проверка повторов
                using (var session = NHibernateHelper.Create().Session)
                {
                    session.FlushMode = FlushMode.Commit;
                    using (var trans = session.BeginTransaction())
                    {
                        foreach (T item in items)
                            try
                            {
                                session.Save(item);
                                additems.Add(item);
                            }
                            catch (Exception ex)
                            {
                                if (ex.InnerException != null)
                                {
                                    if (ex.InnerException.Message.Contains(" not unique"))
                                    {
                                        if(!repeats)
                                            repeats = true;
                                    }
                                    else
                                    {
                                        trans.Rollback();
                                        return false;
                                    }
                                }
                            }
                        if (!repeats)
                        {
                            trans.Commit();
                            return true;
                        }
                        trans.Rollback();
                    }
                }
                #endregion

                #region вставка без повторов
                using (var session = NHibernateHelper.Create().Session)
                {
                    using (var trans = session.BeginTransaction())
                    {
                        try
                        {
                            foreach (T item in additems)
                                session.Save(item);
                        }
                        catch (Exception)
                        {
                            trans.Rollback();
                            return false;
                        }
                        
                        try
                        {
                            trans.Commit();
                        }
                        catch
                        {
                            trans.Rollback();
                            return false;
                        }
                    }
                }
                #endregion
            }
            catch (Exception)
            {
                return false;
            }
            var dt2 = DateTime.Now;
            TimeSpan ts = dt2 - dt1;
            return true;
        }

        internal bool FindExistingRowsInDevLogs(List<DeviceLog> rowsToFind)
        {
            //const string queryString = "SELECT COUNT(*) FROM DeviceLog "
            //                           + "WHERE Severity = :sev "
            //                           + "AND Name = :name "
            //                           + "AND Source = :src "
            //                           + "AND Details = :det "
            //                           + "AND Time = :time "
            //                           + "AND DevId = :devid";

            const string queryString = "FROM DeviceLog "
                                       + "WHERE Severity = :sev "
                                       + "AND Name = :name "
                                       + "AND Source = :src "
                                       + "AND Details = :det "
                                       + "AND Time = :time "
                                       + "AND DevId = :devid";

            try
            {
                using (var session = NHibernateHelper.Create().Session)
                {
                    using (var trans = session.BeginTransaction())
                    {
                        try
                        {
                            foreach (DeviceLog row in rowsToFind)
                            {
                                //var result = session.CreateQuery(queryString).SetString("sev", row.Severity)
                                //                                             .SetString("name", row.Name)
                                //                                             .SetString("src", row.Source)
                                //                                             .SetString("det", row.Details)
                                //                                             .SetDateTime("time", row.Time)
                                //                                             .SetInt32("devid", row.DevId).UniqueResult<Int64>();

                                var result = session.CreateQuery(queryString).SetString("sev", row.Severity)
                                                                             .SetString("name", row.Name)
                                                                             .SetString("src", row.Source)
                                                                             .SetString("det", row.Details)
                                                                             .SetDateTime("time", row.Time)
                                                                             .SetInt32("devid", row.DevId).List();

                                if (result.Count == 0)
                                {
                                    //addItems.Add(row);
                                    session.Save(row);
                                }
                                else
                                {
                                    var deviceLog = result[0] as DeviceLog;
                                    if (deviceLog != null && deviceLog.EndTime == null && row.EndTime != null)
                                    {
                                        deviceLog.EndTime = row.EndTime;
                                        session.Update(deviceLog);
                                    }
                                }
                            }
                            trans.Commit();
                            return true;
                        }
                        catch (Exception)
                        {
                            trans.Rollback();
                            return false;
                        }
                    }
                }
            }
            catch (Exception)
            {
                return false;
            }
        }

        public bool Remove(T item)
        {
            string getQueryString = "";
            var repositoryName = item as IRepositoryName;
            if (repositoryName != null)
            {
                string name = repositoryName.Name;
                getQueryString = string.Format("SELECT Id FROM {0} WHERE Name = '{1}'", item.GetType().Name, name);
            }
            if (item is IRepositoryDevicelibInherit)
            {
                var idrtps = (item as IRepositoryDevicelibInherit).Idrtps;
                var iddevice = (item as IRepositoryDevicelibInherit).Iddevicelib;
                getQueryString = string.Format("SELECT Id FROM {0} WHERE Iddevicelib = '{1}' AND IdRTPS = {2}", item.GetType().Name, iddevice,idrtps);
            }
            try
            {
                using (var session = NHibernateHelper.Create().Session)
                {
                    using (var trans = session.BeginTransaction())
                    {
                        var id = session.CreateQuery(getQueryString).UniqueResult<int>();
                        session.CreateQuery("DELETE FROM " + item.GetType().Name + " WHERE Id = :id")
                            .SetInt32("id", id).ExecuteUpdate();
                        trans.Commit();
                    }
                }
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        public bool Remove(string name, string rtpsName = "-_-")
        {
            var item = new T();
            var repositoryName = item as IRepositoryName;
            if (repositoryName != null)
            {
                repositoryName.Name = name;
            }
            var repositoryDevice = item as IRepositoryDevicelibInherit;
            if (repositoryDevice != null)
            {
                if (rtpsName == "-_-")
                    return false;
                repositoryDevice.Idrtps = (new DBRepository<RTPS>()).GetItem(rtpsName).Id;
                if (name == "-_-")
                    return false;
                repositoryDevice.Iddevicelib = (new DBRepository<DevicesLibs>()).GetItem(name).Id;
            }
            return Remove(item);
        }

        /// <summary>
        /// Обновление записи в БД (не использовать для таблицы devices).
        /// </summary>
        /// <param name="item">Объект, который должен содержать имя, которое есть в БД.</param>
        /// <returns></returns>
        public bool Update(T item)
        {
            var hasName = item as IRepositoryName;
            if (hasName != null)
            {
                return Update(item, hasName.Name);
            }
            //return false;

            var device = item as IRepositoryDevicelibInherit;
            if (device != null)
            {
                return Update(item, "", device.Idrtps, device.Iddevicelib);
            }
            return false;
            //if (item is IRepositoryIdrtps)
            //{
            //    var idrtps = (new DBRepository<RTPS>()).GetItem((item as IRepositoryIdrtps).Idrtps).Name;
            //    return Update((item as IRepositoryName).Name, item, idrtps);
            //}
            //return Update((item as IRepositoryName).Name, item);





            //var name = ((IRepositoryName)item).Name;
            //string getQueryString;
            //if (item is IRepositoryIdrtps)
            //{
            //    var idrtps = (item as IRepositoryIdrtps).Idrtps;
            //    getQueryString = string.Format("SELECT Id FROM {0} WHERE Name = '{1}' AND IdRTPS = {2}", item.GetType().Name, name, idrtps);
            //}
            //else
            //{
            //    getQueryString = string.Format("SELECT Id FROM {0} WHERE Name = '{1}'", item.GetType().Name, name);
            //}
            //try
            //{
            //    using (var session = NHibernateHelper.Create().Session)
            //    {
            //        using (var trans = session.BeginTransaction())
            //        {
            //            var id = session.CreateQuery(getQueryString).UniqueResult<int>();
            //            /*session.CreateQuery("DELETE FROM " + item.GetType().Name + " WHERE Id = :id")
            //                .SetInt32("id", id).ExecuteUpdate();*/
            //            session.Update(item, id);
            //            trans.Commit();
            //        }
            //    }
            //}
            //catch (Exception)
            //{
            //    return false;
            //}
            //return true;

        }

        /// <summary>
        /// Обновление записи в БД.
        /// </summary>
        /// <param name="item">Новый объект.</param>
        /// <param name="oldName">Старое имя объекта. (для таблицы device не используется)</param>
        /// <param name="idrtps">РТПС. (только для таблицы device)</param>
        /// <param name="iddevice">Устройство. (только для таблицы device)</param>
        /// <returns>true - объект обновлен. false - объект не был обновлен.</returns>
        public bool Update(T item, string oldName, int idrtps = -1, int iddevice = -1)
        {
            string getQueryString = "";
            if (item is IRepositoryName)
            {
                getQueryString = string.Format("SELECT Id FROM {0} WHERE Name = '{1}'", item.GetType().Name, oldName);
            }
            if (item is IRepositoryDevicelibInherit)
            {
                if (idrtps == -1)
                    return false;
                if (iddevice == -1)
                    return false;
                getQueryString = string.Format("SELECT Id FROM {0} WHERE Iddevicelib = '{1}' AND IdRTPS = {2}", item.GetType().Name, iddevice, idrtps);
            }
            try
            {
                using (var session = NHibernateHelper.Create().Session)
                {
                    using (var trans = session.BeginTransaction())
                    {
                        var id = session.CreateQuery(getQueryString).UniqueResult<int>();
                        session.Update(item, id);
                        try
                        {
                            trans.Commit();
                        }
                        catch
                        {
                            trans.Rollback();
                            return false;
                        }
                    }
                }
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        public T GetItem(int itemId)
        {
            T item;
            try
            {
                using (var session = NHibernateHelper.Create().Session)
                {
                    using (var trans = session.BeginTransaction())
                    {
                        item = session.Get<T>(itemId);
                        //item = session.CreateQuery("FROM " + item.GetType().Name + " WHERE Id = :id")
                        //              .SetInt32("id", itemId).UniqueResult<T>();
                        trans.Commit();
                    }
                }
            }
            catch (Exception)
            {
                item = null;
            }
            return item;
        }

        public T GetItem(string name/*, int iddevice = -1*/, int idrtps = -1)
        {
            var item = new T();
            string queryString = "";
            //if (idrtps == -1)
            //{
            //    queryString = string.Format("FROM {0} WHERE Name = '{1}'", item.GetType().Name, name);
            //}
            //else
            //{

            //    queryString = string.Format("FROM {0} WHERE Name = '{1}' AND IdRTPS = {2}", item.GetType().Name,
            //                              name, idrtps);
            //}

            if (item is IRepositoryDevicelibInherit)
            {
                var iddevice = new DBRepository<DevicesLibs>().GetItem(name).Id;
                queryString = string.Format("FROM {0} WHERE Iddevicelib = '{1}' AND IdRTPS = {2}", item.GetType().Name,
                                          iddevice, idrtps);
                    //queryString = string.Format("FROM {0} WHERE Iddevice = '{1}' AND IdRTPS = {2}", item.GetType().Name,
                    //                      iddevice, idrtps);
            }
            if (item is IRepositoryName)
            {
                queryString = string.Format("FROM {0} WHERE Name = '{1}'", item.GetType().Name, name);
            }

            try
            {
                using (var session = NHibernateHelper.Create().Session)
                {
                    item = session.CreateQuery(queryString).UniqueResult<T>();
                }
            }
            catch (Exception)
            {
                item = null;
            }
            return item;
        }

        public long GetItemCount()
        {
            long count = -1;
            try
            {
                using (var session = NHibernateHelper.Create().Session)
                {
                    using (var trans = session.BeginTransaction())
                    {
                        count = session.CreateQuery("SELECT count(Id) FROM " + typeof(T)).UniqueResult<long>();
                        trans.Commit();
                    }
                }
            }
            catch (Exception)
            {
                return count;
            }
            return count;
        }

        public List<T> GetList(string criteria = "")
        {
            //-1 - NODB
            //-2 - requesterror
            //-3 - datanotfound
            int count = -1;
            if (criteria.Contains("max:"))
            {
                var split = criteria.Split(';');
                if (split.Length > 1)
                {
                    if (!int.TryParse(split[0].Substring(4), out count))
                        count = -1;
                    criteria = split[1];
                }
                else
                    criteria = string.Empty;

            }
            var list = new List<T>();
            var nhib = NHibernateHelper.Create();
            if (nhib == null)
            {
                var item = new T();
                if (item is IRepositoryName)
                    ((IRepositoryName)item).Name = "NODB";
                else
                {
                    ((IRepositoryDevicelibInherit) item).Iddevicelib = -1;
                }
                list.Add(item);
                return list;
            }
            try
            {
                using (var session = nhib.Session)
                {
                    var query = session.CreateQuery("FROM " + typeof (T) + " " + criteria);
                    if (count != -1)
                        query.SetMaxResults(count);
                    list = (List<T>) query.List<T>();
                }
            }
            catch (NullReferenceException)
            {
                var item = new T();
                if (item is IRepositoryName)
                    ((IRepositoryName)item).Name = "datanotfound";
                else
                {
                    ((IRepositoryDevicelibInherit)item).Iddevicelib = -3;
                }
                list.Add(item);
            }
            catch (Exception)
            {
                var item = new T();
                //((IRepositoryName)item).Name = "requesterror";
                if (item is IRepositoryName)
                    ((IRepositoryName)item).Name = "requesterror";
                else
                {
                    ((IRepositoryDevicelibInherit)item).Iddevicelib = -2;
                }
                list.Add(item);
            }
            return list;
        }

        public List<T> GetListStart(string criteria = "")
        {
            var nhib = NHibernateHelper.Create();
            var list = new List<T>();
            if (nhib == null)
            {
                var item = new T();
                ((IRepositoryName) item).Name = "NODB";
                list.Add(item);
                return list;
            }
            _session = nhib.Session;
            try
            {
                list = (List<T>) _session.CreateQuery("FROM " + typeof (T) + " " + criteria).List<T>();
            }
            catch
            {
                var item = new T();
                ((IRepositoryName)item).Name = "requesterror";
                list.Add(item);
                _session.Close();
            }
            return list;
        }

        public void SessionClose()
        {
            if (IsSessionOpened)
                _session.Close();
        }

        public void Dispose()
        {
            SessionClose();
        }
    }

}
